package com.harbintransport.backend.service;

import com.harbintransport.backend.model.DataProcessingException;
import com.harbintransport.backend.model.HourlyTrafficTrend;
import com.harbintransport.backend.model.RouteMobility;
import com.harbintransport.backend.model.RouteStationFunnel;
import com.harbintransport.backend.model.StationCrowdRanking;
import com.harbintransport.backend.model.StationPassengerRanking;
import com.harbintransport.backend.model.BusRushHourTraffic;
import com.harbintransport.backend.model.SankeyData;
import com.harbintransport.backend.model.SankeyLink;
import com.harbintransport.backend.model.SankeyNode;
import com.harbintransport.backend.model.BusStopFrequencyUsage;
import com.harbintransport.backend.model.DispatchSuggestion;
import com.harbintransport.backend.repository.HourlyTrafficTrendRepository;
import com.harbintransport.backend.repository.OdAnalysisRepository;
import com.harbintransport.backend.repository.RouteStationFunnelRepository;
import com.harbintransport.backend.repository.StationPassengerRankingRepository;
import com.harbintransport.backend.repository.StationCrowdRankingRepository;
import com.harbintransport.backend.repository.BusRushHourTrafficRepository;
import com.harbintransport.backend.repository.RouteMobilityRepository;
import com.harbintransport.backend.repository.BusStopFrequencyUsageRepository;
import com.harbintransport.backend.repository.DispatchSuggestionRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class TrafficDataService {

    @Autowired
    private HourlyTrafficTrendRepository hourlyTrafficTrendRepository;

    @Autowired
    private StationPassengerRankingRepository stationPassengerRankingRepository;

    @Autowired
    private RouteStationFunnelRepository routeStationFunnelRepository;

    @Autowired
    private StationCrowdRankingRepository stationCrowdRankingRepository;

    @Autowired
    private BusRushHourTrafficRepository busRushHourTrafficRepository;

    @Autowired
    private OdAnalysisRepository odAnalysisRepository;

    @Autowired
    private RouteMobilityRepository routeMobilityRepository;

    @Autowired
    private BusStopFrequencyUsageRepository busStopFrequencyUsageRepository;

    @Autowired
    private DispatchSuggestionRepository dispatchSuggestionRepository;

    private static final String ROUTE_MOBILITY_FILE = "../mapreduce_jobs/data/route_passenger_mobility_analysis.tsv";
    private static final String BASE_PATH = "mapreduce_jobs/data/";

    public List<HourlyTrafficTrend> getHourlyTrafficTrends() {
        return hourlyTrafficTrendRepository.findAll();
    }

    public List<StationPassengerRanking> getStationRanking() {
        List<StationPassengerRanking> rankings = stationPassengerRankingRepository.findAll();
        rankings.sort(Comparator.comparingLong(s -> -(s.getOnboard() + s.getOffboard())));
        return rankings;
    }

    public List<RouteStationFunnel> getRouteFunnel(String routeId) {
        return routeStationFunnelRepository.findByRouteId(routeId);
    }

    public List<StationCrowdRanking> getStationCrowdRanking() {
        return stationCrowdRankingRepository.findAll();
    }

    public List<BusRushHourTraffic> getBusRushHourTraffic() {
        return busRushHourTrafficRepository.findAll();
    }

    public List<RouteMobility> getRouteMobility() {
        return routeMobilityRepository.findAll();
    }

    public SankeyData getODSankeyData() {
        List<com.harbintransport.backend.model.OdAnalysis> odData = odAnalysisRepository.findAll();

        List<SankeyLink> links = odData.stream()
                .map(od -> new SankeyLink(od.getSourceStation(), od.getTargetStation(), od.getPassengerCount()))
                .collect(Collectors.toList());

        Set<String> nodeNames = new HashSet<>();
        odData.forEach(od -> {
            nodeNames.add(od.getSourceStation());
            nodeNames.add(od.getTargetStation());
        });

        List<SankeyNode> nodes = nodeNames.stream()
                .map(SankeyNode::new)
                .collect(Collectors.toList());

        return new SankeyData(nodes, links);
    }

    public List<BusStopFrequencyUsage> getBusStopFrequencyUsageData() {
        return busStopFrequencyUsageRepository.findAll();
    }

    public List<DispatchSuggestion> getDispatchSuggestionData(String routeId, String stationId, Integer hour) {
        if (routeId != null && !routeId.isEmpty() && stationId != null && !stationId.isEmpty() && hour != null) {
            return dispatchSuggestionRepository.findByRouteIdAndStationIdAndHour(routeId, stationId, hour);
        } else if (routeId != null && !routeId.isEmpty() && stationId != null && !stationId.isEmpty()) {
            return dispatchSuggestionRepository.findByRouteIdAndStationId(routeId, stationId);
        } else if (routeId != null && !routeId.isEmpty() && hour != null) {
            return dispatchSuggestionRepository.findByRouteIdAndHour(routeId, hour);
        } else if (stationId != null && !stationId.isEmpty() && hour != null) {
            return dispatchSuggestionRepository.findByStationIdAndHour(stationId, hour);
        } else if (routeId != null && !routeId.isEmpty()) {
            return dispatchSuggestionRepository.findByRouteId(routeId);
        } else if (stationId != null && !stationId.isEmpty()) {
            return dispatchSuggestionRepository.findByStationId(stationId);
        } else if (hour != null) {
            return dispatchSuggestionRepository.findByHour(hour);
        } else {
            return dispatchSuggestionRepository.findAll();
        }
    }

    public List<String> getDistinctRouteIds() {
        return dispatchSuggestionRepository.findDistinctRouteIds();
    }

    public List<String> getDistinctStationIdsByRouteId(String routeId) {
        return dispatchSuggestionRepository.findDistinctStationIdsByRouteId(routeId);
    }
}